﻿Public Class MySkin

    '事件
    Public Event Click(sender As Object, e As MouseButtonEventArgs)

    '皮肤储存
    Private _Address As String
    Public Property Address() As String
        Get
            Return _Address
        End Get
        Set(value As String)
            _Address = value
            ToolTip = If(_Address = "", "加载中", "点击更换皮肤（右键查看更多选项）")
        End Set
    End Property
    Public Loader As LoaderTask(Of EqualableList(Of String), String)

    '控件动画
    Private Sub PanSkin_MouseEnter(sender As Object, e As MouseEventArgs) Handles Me.MouseEnter
        AniStart(AaOpacity(ShadowSkin, 0.8 - ShadowSkin.Opacity, 200, 100), "Skin Shadow")
    End Sub
    Private Sub PanSkin_MouseLeave(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
        AniStart(AaOpacity(ShadowSkin, 0.2 - ShadowSkin.Opacity, 200), "Skin Shadow")
        IsSkinMouseDown = False
        AniStart(AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
    End Sub

    '点击
    Private IsSkinMouseDown As Boolean = False
    Private Sub PanSkin_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
        IsSkinMouseDown = True
        AniStart(AaScaleTransform(Me, 0.9 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
    End Sub
    Private Sub PanSkin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
        AniStart(AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, 60,, New AniEaseOutFluent), "Skin Scale")
        If IsSkinMouseDown Then
            IsSkinMouseDown = False
            RaiseEvent Click(sender, e)
        End If
    End Sub

    '保存皮肤
    Private Sub BtnSkinSave_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinSave.Click
        Save(Loader)
    End Sub
    Public Shared Sub Save(Loader As LoaderTask(Of EqualableList(Of String), String))
        Dim Address = Loader.Output
        If Not Loader.State = LoadState.Finished Then
            Hint("皮肤正在获取中，请稍候！", HintType.Critical)
            If Not Loader.State = LoadState.Loading Then Loader.Start()
            Exit Sub
        End If
        Try
            Dim FileAddress As String = SelectAs("选取保存皮肤的位置", GetFileNameFromPath(Address), "皮肤图片文件(*.png)|*.png")
            If FileAddress.Contains("\") Then
                File.Delete(FileAddress)
                If Address.StartsWith(PathImage) Then
                    Dim Image As New MyBitmap(Address)
                    Image.Save(FileAddress)
                Else
                    File.Copy(Address, FileAddress)
                End If
                Hint("皮肤保存成功！", HintType.Finish)
            End If
        Catch ex As Exception
            Log(ex, "保存皮肤失败", LogLevel.Hint)
        End Try
    End Sub
    Private Sub BtnSkinSave_Checked(sender As MyMenuItem, e As RoutedEventArgs) Handles BtnSkinSave.Checked
        sender.IsEnabled = Address = ""
    End Sub

    ''' <summary>
    ''' 载入皮肤。
    ''' </summary>
    Public Sub Load()
        Try
            Address = Loader.Output
            '检查文件存在
            If Address = "" Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
            If Not Address.StartsWith(PathImage) AndAlso Not File.Exists(Address) Then Throw New FileNotFoundException("皮肤文件未找到", Address)
            '加载
            Dim Image As New MyBitmap(Address)
            ImgBack.Tag = Address
            '头发层（附加层）
            If Image.Pic.Width >= 64 AndAlso Image.Pic.Height >= 32 Then
                '如果图片中有透明像素，或是头部颜色和透明区均不一样，则加载附加层
                If (Image.Pic.GetPixel(1, 1).A = 0 OrElse Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1).A = 0 OrElse Image.Pic.GetPixel(Image.Pic.Width - 2, 2).A = 0) OrElse
                   (Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(41, 9) AndAlso Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(41, 9) AndAlso Image.Pic.GetPixel(Image.Pic.Width - 2, 2) <> Image.Pic.GetPixel(41, 9)) Then
                    ImgFore.Source = New CroppedBitmap(Image, New Int32Rect(40, 8, 8, 8))
                Else
                    ImgFore.Source = Nothing
                End If
            Else
                ImgFore.Source = Nothing
            End If
            '脸层
            If Image.Pic.Width >= 32 AndAlso Image.Pic.Height >= 32 Then
                ImgBack.Source = New CroppedBitmap(Image, New Int32Rect(8, 8, 8, 8))
                Log("[Skin] 载入头像成功：" & Loader.Name)
            Else
                ImgBack.Source = Nothing
                Throw New Exception("图片大小不足，长为 " & Image.Pic.Height & "，宽为 " & Image.Pic.Width)
            End If
        Catch ex As Exception
            Log(ex, "载入头像失败（" & If(Address, "null") & "," & Loader.Name & "）", LogLevel.Hint)
        End Try
    End Sub
    ''' <summary>
    ''' 清空皮肤。
    ''' </summary>
    Public Sub Clear()
        Address = ""
        ImgFore.Source = Nothing
        ImgBack.Source = Nothing
    End Sub

    '刷新缓存
    Private Sub RefreshClick(sender As MyMenuItem, e As EventArgs) Handles BtnSkinRefresh.Click
        RefreshCache(Loader)
    End Sub
    ''' <summary>
    ''' 刷新皮肤缓存。
    ''' </summary>
    Public Shared Sub RefreshCache(Optional sender As LoaderTask(Of EqualableList(Of String), String) = Nothing)
        Dim HasLoaderRunning As Boolean = False
        For Each SkinLoader In PageLaunchLeft.SkinLoaders
            If SkinLoader.State = LoadState.Loading Then
                HasLoaderRunning = True : Exit For
            End If
        Next
        If FrmLaunchLeft IsNot Nothing AndAlso HasLoaderRunning Then
            '由于 Abort 不是实时的，暂时不会释放文件，会导致删除报错，故只能取消执行
            Hint("有正在获取中的皮肤，请稍后再试！", HintType.Info)
        Else
            RunInThread(Sub()
                            Try
                                Hint("正在刷新皮肤……")
                                '清空缓存
                                Log("[Skin] 正在清空皮肤缓存")
                                If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
                                If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
                                IniClearCache(PathTemp & "Cache\Skin\IndexMojang.ini")
                                IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
                                IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
                                IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
                                IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
                                '刷新控件
                                For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
                                    SkinLoader.WaitForExit(IsForceRestart:=True)
                                Next
                                Hint("已刷新皮肤！", HintType.Finish)
                            Catch ex As Exception
                                Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
                            End Try
                        End Sub)
        End If
    End Sub
    ''' <summary>
    ''' 在更换正版皮肤后，刷新正版皮肤。
    ''' </summary>
    ''' <param name="SkinAddress">新的正版皮肤完整地址。</param>
    Public Shared Sub ReloadCache(SkinAddress As String, IsMsLogin As Boolean)
        RunInThread(Sub()
                        Try
                            '更新缓存
                            Dim LoginType As String = If(IsMsLogin, "Ms", "Mojang")
                            WriteIni(PathTemp & "Cache\Skin\Index" & LoginType & ".ini", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress)
                            Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress))
                            '刷新控件
                            For Each SkinLoader In If(IsMsLogin, {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy})
                                SkinLoader.WaitForExit(IsForceRestart:=True)
                            Next
                            '完成提示
                            Hint("更改皮肤成功！", HintType.Finish)
                        Catch ex As Exception
                            Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
                        End Try
                    End Sub)
    End Sub

    '披风
    Public Property HasCape As Boolean
        Get
            Return BtnSkinCape.Visibility = Visibility.Collapsed
        End Get
        Set(value As Boolean)
            If value Then
                BtnSkinCape.Visibility = Visibility.Visible
            Else
                BtnSkinCape.Visibility = Visibility.Collapsed
            End If
        End Set
    End Property
    Private IsChanging As Boolean = False
    Private Sub BtnSkinCape_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinCape.Click
        '检查条件，获取新披风
        If IsChanging Then
            Hint("正在更改披风中，请稍候！")
            Exit Sub
        End If
        If McLoginMsLoader.State = LoadState.Failed Then
            Hint("登录失败，无法更改披风！", HintType.Critical)
            Exit Sub
        End If
        Hint("正在获取披风列表，请稍候……")
        IsChanging = True
        '开始实际获取
        RunInNewThread(Sub()
                           Try
Retry:
                               '获取登录信息
                               If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit() '等待登录结束
                               If McLoginMsLoader.State <> LoadState.Finished Then
                                   Hint("登录失败，无法更改披风！", HintType.Critical)
                                   Exit Sub
                               End If
                               Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
                               Dim Uuid As String = McLoginMsLoader.Output.Uuid
                               Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
                               '获取玩家的所有披风
                               Dim SelId As Integer? = Nothing
                               RunInUiWait(Sub()
                                               Try
                                                   Dim CapeNames As New Dictionary(Of String, String) From {
                                                       {"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"}, {"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}
                                                   }
                                                   Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
                                                   For Each Cape In SkinData("capes")
                                                       Dim CapeName As String = Cape("alias").ToString
                                                       If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
                                                       SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
                                                   Next
                                                   SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
                                               Catch ex As Exception
                                                   Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
                                               End Try
                                           End Sub)
                               If SelId Is Nothing Then Exit Sub
                               '发送请求
                               Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
                                                                       If(SelId = 0, "DELETE", "PUT"),
                                                                       If(SelId = 0, "", "{""capeId"": """ & SkinData("capes")(SelId - 1)("id").ToString & """}"),
                                                                       "application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
                               If Result.Contains("""errorMessage""") Then
                                   Hint("更改披风失败：" & GetJson(Result)("errorMessage"), HintType.Critical)
                                   Exit Sub
                               Else
                                   Hint("更改披风成功！", HintType.Finish)
                               End If
                           Catch ex As Exception
                               Log(ex, "更改披风失败", LogLevel.Hint)
                           Finally
                               IsChanging = False
                           End Try
                       End Sub, "Cape Change")
    End Sub

End Class
